﻿using UnityEngine;
using System.Text;
using System.Collections.Generic;
using System.IO;

/// <summary>
/// MemoryStream.ReadLine has an interesting oddity: it doesn't always advance the stream's position by the correct amount:
/// http://social.msdn.microsoft.com/Forums/en-AU/Vsexpressvcs/thread/b8f7837b-e396-494e-88e1-30547fcf385f
/// Solution? Custom line reader with the added benefit of not having to use streams at all.
/// </summary>
public class UGUIByteReader
{
    byte[] mBuffer;
    int mOffset = 0;

    public UGUIByteReader(byte[] bytes) { mBuffer = bytes; }
    public UGUIByteReader(TextAsset asset) { mBuffer = asset.bytes; }

    /// <summary>
    /// Read the contents of the specified file and return a Byte Reader to work with.
    /// </summary>

    static public UGUIByteReader Open(string path)
    {
#if UNITY_EDITOR || (!UNITY_FLASH && !NETFX_CORE && !UNITY_WP8 && !UNITY_WP_8_1)
        FileStream fs = File.OpenRead(path);

        if (fs != null)
        {
            fs.Seek(0, SeekOrigin.End);
            byte[] buffer = new byte[fs.Position];
            fs.Seek(0, SeekOrigin.Begin);
            fs.Read(buffer, 0, buffer.Length);
            fs.Close();
            return new UGUIByteReader(buffer);
        }
#endif
        return null;
    }

    /// <summary>
    /// Whether the buffer is readable.
    /// </summary>

    public bool canRead { get { return (mBuffer != null && mOffset < mBuffer.Length); } }

    /// <summary>
    /// Read a single line from the buffer.
    /// </summary>

    static string ReadLine(byte[] buffer, int start, int count)
    {
#if UNITY_FLASH
		// Encoding.UTF8 is not supported in Flash :(
		StringBuilder sb = new StringBuilder();

		int max = start + count;

		for (int i = start; i < max; ++i)
		{
			byte byte0 = buffer[i];

			if ((byte0 & 128) == 0)
			{
				// If an UCS fits 7 bits, its coded as 0xxxxxxx. This makes ASCII character represented by themselves
				sb.Append((char)byte0);
			}
			else if ((byte0 & 224) == 192)
			{
				// If an UCS fits 11 bits, it is coded as 110xxxxx 10xxxxxx
				if (++i == count) break;
				byte byte1 = buffer[i];
				int ch = (byte0 & 31) << 6;
				ch |= (byte1 & 63);
				sb.Append((char)ch);
			}
			else if ((byte0 & 240) == 224)
			{
				// If an UCS fits 16 bits, it is coded as 1110xxxx 10xxxxxx 10xxxxxx
				if (++i == count) break;
				byte byte1 = buffer[i];
				if (++i == count) break;
				byte byte2 = buffer[i];

				if (byte0 == 0xEF && byte1 == 0xBB && byte2 == 0xBF)
				{
					// Byte Order Mark -- generally the first 3 bytes in a Windows-saved UTF-8 file. Skip it.
				}
				else
				{
					int ch = (byte0 & 15) << 12;
					ch |= (byte1 & 63) << 6;
					ch |= (byte2 & 63);
					sb.Append((char)ch);
				}
			}
			else if ((byte0 & 248) == 240)
			{
				// If an UCS fits 21 bits, it is coded as 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
				if (++i == count) break;
				byte byte1 = buffer[i];
				if (++i == count) break;
				byte byte2 = buffer[i];
				if (++i == count) break;
				byte byte3 = buffer[i];

				int ch = (byte0 & 7) << 18;
				ch |= (byte1 & 63) << 12;
				ch |= (byte2 & 63) << 6;
				ch |= (byte3 & 63);
				sb.Append((char)ch);
			}
		}
		return sb.ToString();
#else
        return Encoding.UTF8.GetString(buffer, start, count);
#endif
    }

    /// <summary>
    /// Read a single line from the buffer.
    /// </summary>

    public string ReadLine() { return ReadLine(true); }

    /// <summary>
    /// Read a single line from the buffer.
    /// </summary>

    public string ReadLine(bool skipEmptyLines)
    {
        int max = mBuffer.Length;

        // Skip empty characters
        if (skipEmptyLines)
        {
            while (mOffset < max && mBuffer[mOffset] < 32) ++mOffset;
        }

        int end = mOffset;

        if (end < max)
        {
            for (;;)
            {
                if (end < max)
                {
                    int ch = mBuffer[end++];
                    if (ch != '\n' && ch != '\r') continue;
                }
                else ++end;

                string line = ReadLine(mBuffer, mOffset, end - mOffset - 1);
                mOffset = end;
                return line;
            }
        }
        mOffset = max;
        return null;
    }

    /// <summary>
    /// Assume that the entire file is a collection of key/value pairs.
    /// </summary>

    public Dictionary<string, string> ReadDictionary()
    {
        Dictionary<string, string> dict = new Dictionary<string, string>();
        char[] separator = new char[] { '=' };

        while (canRead)
        {
            string line = ReadLine();
            if (line == null) break;
            if (line.StartsWith("//", System.StringComparison.Ordinal)) continue;

#if UNITY_FLASH
			string[] split = line.Split(separator, System.StringSplitOptions.RemoveEmptyEntries);
#else
            string[] split = line.Split(separator, 2, System.StringSplitOptions.RemoveEmptyEntries);
#endif

            if (split.Length == 2)
            {
                string key = split[0].Trim();
                string val = split[1].Trim().Replace("\\n", "\n");
                dict[key] = val;
            }
        }
        return dict;
    }

    static UGUIBetterList<string> mTemp = new UGUIBetterList<string>();

    /// <summary>
    /// Read a single line of Comma-Separated Values from the file.
    /// </summary>

    public UGUIBetterList<string> ReadCSV()
    {
        mTemp.Clear();
        string line = "";
        bool insideQuotes = false;
        int wordStart = 0;

        while (canRead)
        {
            if (insideQuotes)
            {
                string s = ReadLine(false);
                if (s == null) return null;
                s = s.Replace("\\n", "\n");
                line += "\n" + s;
            }
            else
            {
                line = ReadLine(true);
                if (line == null) return null;
                line = line.Replace("\\n", "\n");
                wordStart = 0;
            }

            for (int i = wordStart, imax = line.Length; i < imax; ++i)
            {
                char ch = line[i];

                if (ch == ',')
                {
                    if (!insideQuotes)
                    {
                        mTemp.Add(line.Substring(wordStart, i - wordStart));
                        wordStart = i + 1;
                    }
                }
                else if (ch == '"')
                {
                    if (insideQuotes)
                    {
                        if (i + 1 >= imax)
                        {
                            mTemp.Add(line.Substring(wordStart, i - wordStart).Replace("\"\"", "\""));
                            return mTemp;
                        }

                        if (line[i + 1] != '"')
                        {
                            mTemp.Add(line.Substring(wordStart, i - wordStart).Replace("\"\"", "\""));
                            insideQuotes = false;

                            if (line[i + 1] == ',')
                            {
                                ++i;
                                wordStart = i + 1;
                            }
                        }
                        else ++i;
                    }
                    else
                    {
                        wordStart = i + 1;
                        insideQuotes = true;
                    }
                }
            }

            if (wordStart < line.Length)
            {
                if (insideQuotes) continue;
                mTemp.Add(line.Substring(wordStart, line.Length - wordStart));
            }
            return mTemp;
        }
        return null;
    }
}